#ifndef EMS_PROTOCOL_MQTT_MQTT_CLIENT_H
#define EMS_PROTOCOL_MQTT_MQTT_CLIENT_H

#include <string>
#include <functional>
#include <memory>
#include <thread>
#include <atomic>
#include <mutex>
#include <queue>

struct mosquitto;
struct mosquitto_message;

namespace ems {
namespace protocol {

enum class MqttQoS {
    QOS_0 = 0,
    QOS_1 = 1,
    QOS_2 = 2
};

struct MqttMessage {
    std::string topic;
    std::string payload;
    MqttQoS qos;
    bool retain;
    
    MqttMessage() : qos(MqttQoS::QOS_0), retain(false) {}
    MqttMessage(const std::string& t, const std::string& p, MqttQoS q = MqttQoS::QOS_0, bool r = false)
        : topic(t), payload(p), qos(q), retain(r) {}
};

class MqttClient {
public:
    using MessageCallback = std::function<void(const std::string& topic, const std::string& payload)>;
    using ConnectCallback = std::function<void(bool connected)>;
    using PublishCallback = std::function<void(int messageId, bool success)>;

    MqttClient();
    virtual ~MqttClient();
    
    bool initialize(const std::string& clientId, const std::string& host, int port);
    bool setCredentials(const std::string& username, const std::string& password);
    bool setTLS(const std::string& caFile, const std::string& certFile, const std::string& keyFile);
    
    void setMessageCallback(MessageCallback callback) { messageCallback_ = callback; }
    void setConnectCallback(ConnectCallback callback) { connectCallback_ = callback; }
    void setPublishCallback(PublishCallback callback) { publishCallback_ = callback; }
    
    void setKeepAlive(int keepAlive) { keepAlive_ = keepAlive; }
    void setCleanSession(bool cleanSession) { cleanSession_ = cleanSession; }
    void setAutoReconnect(bool autoReconnect) { autoReconnect_ = autoReconnect; }
    void setReconnectDelay(int delayMs) { reconnectDelay_ = delayMs; }
    
    bool connect();
    void disconnect();
    bool isConnected() const;
    
    bool subscribe(const std::string& topic, MqttQoS qos = MqttQoS::QOS_1);
    bool unsubscribe(const std::string& topic);
    
    bool publish(const std::string& topic, const std::string& payload, 
                MqttQoS qos = MqttQoS::QOS_1, bool retain = false);
    bool publishAsync(const std::string& topic, const std::string& payload, 
                     MqttQoS qos = MqttQoS::QOS_1, bool retain = false);
    
    void startLoop();
    void stopLoop();
    
    std::string getLastError() const;

private:
    mosquitto* client_;
    std::string clientId_;
    std::string host_;
    int port_;
    std::string username_;
    std::string password_;
    std::string caFile_;
    std::string certFile_;
    std::string keyFile_;
    
    int keepAlive_;
    bool cleanSession_;
    bool autoReconnect_;
    int reconnectDelay_;
    
    std::atomic<bool> connected_;
    std::atomic<bool> loopRunning_;
    std::thread loopThread_;
    std::thread reconnectThread_;
    
    MessageCallback messageCallback_;
    ConnectCallback connectCallback_;
    PublishCallback publishCallback_;
    
    mutable std::mutex errorMutex_;
    mutable std::string lastError_;
    
    std::mutex publishQueueMutex_;
    std::queue<MqttMessage> publishQueue_;
    
    void loopForever();
    void reconnectLoop();
    void processPublishQueue();
    
    void setLastError(const std::string& error) const;
    
    static void onConnect(mosquitto* client, void* userdata, int result);
    static void onDisconnect(mosquitto* client, void* userdata, int result);
    static void onMessage(mosquitto* client, void* userdata, const mosquitto_message* message);
    static void onPublish(mosquitto* client, void* userdata, int messageId);
    static void onSubscribe(mosquitto* client, void* userdata, int messageId, int qosCount, const int* grantedQos);
    static void onUnsubscribe(mosquitto* client, void* userdata, int messageId);
    static void onLog(mosquitto* client, void* userdata, int level, const char* str);
};

}  // namespace protocol
}  // namespace ems

#endif  // EMS_PROTOCOL_MQTT_MQTT_CLIENT_H